// 
// Bachelor of Software Engineering
// Media Design School
// Auckland
// New Zealand
//
// (c) 2005 - 2014 Media Design School
//
// File Name	: SolitaireLogic.cpp
// Desrciption	: CSolitaireLogic implementation file.
// Author(s)	: Hayden Asplet.
// Mail			: hayden.asplet@mediadesignschool.com
//

// PCH
#include "SolitaireStd.h"

// Library Includes

// This Include
#include "SolitaireLogic.h"

// Local Includes
#include "Msvc\resource.h"

// Namespace
using namespace std;

// Global Variables
int m_iCardDrawAmount = 1;

CSolitaireLogic::CSolitaireLogic()
	:m_pDraggedFromStack(0),
	m_iScore(0),
	m_iCardDrawAmount(0),
	m_iCardDrawQueuedAmount(0),
	m_bHasWon(false)
{
	cout << "The Console For Debugging The Solitaire Remake 2014" << endl;
}

CSolitaireLogic::~CSolitaireLogic()
{

}

bool CSolitaireLogic::VInit()
{
	// Load sprites.
	m_background = g_pApp->LoadSprite(IDB_BACKGROUND);
	m_cardSprites = g_pApp->LoadSprite(IDB_CARD_SHEET);
	m_cardBackSprite = g_pApp->LoadSprite(IDB_CARD_BACK);

	// Start with a card draw of one.
	m_iCardDrawAmount = 1;
	m_iCardDrawQueuedAmount = 1;

	SetupGame();

	return true;
}

void CSolitaireLogic::VUpdate(float _fDeltaTime) 
{
	CGameLogic::VUpdate(_fDeltaTime);	// Updates the elapsed time.

	if(m_bHasWon) {
	// The player has won.
		// Get the final score.
		if(m_fElapsedTime >= g_kiSCORE_TIME_THRESHOLD) {
			// Calculate bonus points.
			m_iScore += static_cast<int>(g_kiSCORE_TIME_DIVISION / m_fElapsedTime);
		}

		std::wstring wsWinTextOut = L"You win! Your score is " + std::to_wstring(static_cast<long long>(m_iScore));
		MessageBox(g_pApp->GetHwnd(), wsWinTextOut.c_str(), L"You win!", MB_OK);

		SetupGame();
	}

	// Dragging and dropping.
	if(g_pApp->IsDragging()) {
		if(!m_draggingStack.IsEmpty()) {
			// Update the position of the current dragging stack.
			m_draggingStack.SetPosition(g_pApp->GetMousePosition() - m_dragOffset);
		}
	}

	// Check to see if a first lerping cards are in place.
	while(!m_lerpingStacks.empty()) {
		if(m_lerpingStacks.begin()->IsInPlace()) {
		// The the first card in the queue is in place.
			// Push it onto it's destination.
			m_lerpingStacks.begin()->PushOntoDest();
			
			// Remove from list.
			m_lerpingStacks.pop_front();
		}
		else {
		// Not in place.
			// Stop looping.
			break;
		}
	}

	// Update lerping cards.
	std::list<CLerpStack>::iterator iter;

	for(iter = m_lerpingStacks.begin(); iter != m_lerpingStacks.end(); ++iter) {
		// Update.
		iter->Update(_fDeltaTime);
	}

	// Make sure score doesn't go negative.
	if(m_iScore < 0) {
		m_iScore = 0;
	}
	else {
	// Score is positive.
	}

	// Draw the background.
	g_pApp->RenderSprite(m_background, 0, 0);

	// Draw the stacks.
	for(unsigned int i = 0; i < m_buildStacks.size(); ++i) {
		DrawStack(m_buildStacks[i]);
	}

	for(unsigned int i = 0; i < m_winStacks.size(); ++i) {
		DrawStack(m_winStacks[i]);
	}

	DrawStack(m_deckStack);
	DrawStack(m_wasteStack);

	std::list<CLerpStack>::reverse_iterator rIter;

	// Render lerping stacks.
	for(rIter = m_lerpingStacks.rbegin(); rIter != m_lerpingStacks.rend(); ++rIter) {
		// Render.
		DrawStack(*rIter);
	}

	DrawStack(m_draggingStack);		// Dragging stack is always drawn last.

	// Render the elasped time and score.
	int iElapsedTime = static_cast<int>(m_fElapsedTime);
	std::wstring wsTextOut = L"Time: " + std::to_wstring(static_cast<long long>(iElapsedTime));
	wsTextOut += L"     Score: " + std::to_wstring(static_cast<long long>(m_iScore));

	g_pApp->RenderText(wsTextOut, g_pApp->GetScreenSize() - CPoint(wsTextOut.size() * g_kiCHARACTER_WIDTH, g_kiSCORE_OFFSET));

	// Check to see if the player has won.
	m_bHasWon = true;

	for(unsigned int i = 0; i < m_winStacks.size(); ++i) {
		if(m_winStacks[i].IsEmpty()) {
			m_bHasWon = false;
			break;
		}
		else if(m_winStacks[i].Peek().GetValue() != keKing) {
			m_bHasWon = false;
			break;
		}
		else {
		// This stack is complete.
			// Continue looking through winning stacks.
		}
	}
}

void CSolitaireLogic::VOnLMBPressed()
{
	if(g_pApp->IsDragging()) {
		return;
	}

	// Immediately finish all lerping stacks.
	while(!m_lerpingStacks.empty()) {
		m_lerpingStacks.begin()->PushOntoDest();
		m_lerpingStacks.pop_front();
	}

	cout << "Waste Card Pile Size: [" << m_wasteStack.Size() << "]" << endl;

	// The flip the card from the draw pile to the drawn pile feature
	if(m_deckStack.IsOverlapping(g_pApp->GetMousePosition())) {
		if(m_deckStack.IsEmpty()) {
		// Deck is empty, we need to recycle.
			// Place drawn cards back into the deck in reverse order.
			float fLerpWaitTime = 0;

			for(int i = m_wasteStack.Size()- 1; i >= 0; --i) {
				m_wasteStack[i].Flip();
				m_lerpingStacks.push_back(CLerpStack(m_wasteStack[i], &m_deckStack, fLerpWaitTime));

				fLerpWaitTime += g_kfSTANDARD_LERP_WAITTIME / 5;
			}

			// Update score.
			if(!m_wasteStack.IsEmpty() && m_iCardDrawAmount == 1) {
				m_iScore += g_kiSCORE_RECYCLE;
			}

			// Empty the waste stack.
			m_wasteStack.Clear();
		}
		else {
			float fLerpWaitTime = 0;

			for(int i = 0; i < m_iCardDrawAmount; ++i) {
				if(!m_deckStack.IsEmpty()) {
					// Flip the card.
					//m_deckStack.Peek().Flip();

					m_lerpingStacks.push_back(CLerpStack(m_deckStack.Peek(), &m_wasteStack, fLerpWaitTime, true));

					fLerpWaitTime += g_kfSTANDARD_LERP_WAITTIME;

					// Remove the card from the deck.
					m_deckStack.Pop();
				}
			}

			m_deckStack.VUpdatePositions();
		}
	}

	// Flip face down card on the top of build stacks.
	for(unsigned int i = 0; i < m_buildStacks.size(); ++i) {
		if(m_buildStacks[i].IsOverlapping(g_pApp->GetMousePosition())) {
			if(!m_buildStacks[i].IsEmpty()) {
				if(m_buildStacks[i].Peek().IsFaceDown()) {
					m_buildStacks[i].Peek().Flip();

					// Add to score.
					m_iScore += g_kiSCORE_TURNOVER;
				}
			}
		}
	}
}

void CSolitaireLogic::VOnRMBPressed()
{
	// Immediately finish all lerping stacks.
	while(!m_lerpingStacks.empty()) {
		m_lerpingStacks.begin()->PushOntoDest();
		m_lerpingStacks.pop_front();
	}

	return;
}

void CSolitaireLogic::VOnLMBReleased()
{
	return;
}

void CSolitaireLogic::VOnRMBReleased()
{
	// Immediately finish all lerping stacks.
	while(!m_lerpingStacks.empty()) {
		m_lerpingStacks.begin()->PushOntoDest();
		m_lerpingStacks.pop_front();
	}

	if(g_pApp->IsDragging()) {
		return;
	}

	float fLerpWaitTime = 0;
	bool bCardMoved = true;

	// Take every eligible card in view and move it to the win stack.
	while(bCardMoved) {		// Keep looping until we are done moving cards.
		bCardMoved = false;

		// Check with and move from waste stack first.
		if(!m_wasteStack.IsEmpty()) {
			for(unsigned int i = 0; i < m_winStacks.size(); ++i) {
				if(m_winStacks[i].CanBePushed(m_wasteStack.Peek())) {
				// Card can be placed on the win stack.
					bCardMoved = true;					// Another loop is necessary.
					// Push card on the win stack.
					//m_winStacks[i].Push(m_wasteStack.Peek());
					m_lerpingStacks.push_back(CLerpStack(m_wasteStack.Peek(), &m_winStacks[i], fLerpWaitTime));
					
					// Increment lerp wait time, so that cards move one by one.
					fLerpWaitTime += g_kfSTANDARD_LERP_WAITTIME;

					// Pop from the drawn stack and update positions.
					m_wasteStack.Pop();
					m_wasteStack.VUpdatePositions();

					// Add to score.
					m_iScore += g_kiSCORE_WASTE_WIN;

					// Found auto-placement, no need to continue.
					break;
				}
			}
		}

		for(unsigned int i = 0; i < m_buildStacks.size(); ++i) {
			if(!m_buildStacks[i].IsEmpty()) {
				for(unsigned int j = 0; j < m_winStacks.size(); ++j) {
					CPlayingCard topOfBuild = m_buildStacks[i].Peek();

					if(topOfBuild.IsFaceUp() && m_winStacks[j].CanBePushed(topOfBuild)) {
					// This card can be completed.
						bCardMoved = true;					// Another loop is necessary.
						// Push card on the win stack.
						m_lerpingStacks.push_back(CLerpStack(m_buildStacks[i].Peek(), &m_winStacks[j], fLerpWaitTime));

						// Increment lerp wait time, so that cards move one by one.
						fLerpWaitTime += g_kfSTANDARD_LERP_WAITTIME;

						// Pop from the drawn stack.
						m_buildStacks[i].Pop();

						// Add to score.
						m_iScore += g_kiSCORE_BUILD_WIN;

						// Found auto-placement, no need to continue.
						break;
					}
				}
			}
		}
	}
}

void CSolitaireLogic::VOnLDoubleClick()
{
	// Immediately finish all lerping stacks.
	while(!m_lerpingStacks.empty()) {
		m_lerpingStacks.begin()->PushOntoDest();
		m_lerpingStacks.pop_front();
	}

	if(g_pApp->IsDragging()) {
		return;
	}

	// Automatically place card into win stack.
	if(m_wasteStack.IsOverlapping(g_pApp->GetMousePosition()) && !m_wasteStack.IsEmpty()) {
	// Player is right clicking on the drawn stack.
		// Loop through win stacks to find auto placement
		for(unsigned int i = 0; i < m_winStacks.size(); ++i) {
			if(m_winStacks[i].CanBePushed(m_wasteStack.Peek())) {
			// Card can be placed on the win stack.
				// Push card on the win stack.
				m_lerpingStacks.push_back(CLerpStack(m_wasteStack.Peek(), &m_winStacks[i]));

				// Pop from the drawn stack.
				m_wasteStack.Pop();
				//m_wasteStackFull.Pop();

				// Add to score.
				m_iScore += g_kiSCORE_WASTE_WIN;

				// Found auto-placement, no need to continue.
				return;
			}
		}

		m_wasteStack.VUpdatePositions();
	}
	else {
		for(unsigned int i = 0; i < m_buildStacks.size(); ++i) {
			if(m_buildStacks[i].IsOverlapping(g_pApp->GetMousePosition()) && 
			   !m_buildStacks[i].IsEmpty() &&
			   m_buildStacks[i].Peek().IsFaceUp()) {
			// Player is right clicking on a card on the top of a build stack.
				// Loop through the win stacks to find auto placement.
				for(unsigned int j = 0; j < m_winStacks.size(); ++j) {
					if(m_winStacks[j].CanBePushed(m_buildStacks[i].Peek())) {
					// Card can be pushed onto the win stack.
						// Push card onto the win stack.
						m_lerpingStacks.push_back(CLerpStack(m_buildStacks[i].Peek(), &m_winStacks[j]));

						// Pop from the build stack.
						m_buildStacks[i].Pop();

						// Add to score.
						m_iScore += g_kiSCORE_BUILD_WIN;

						// Found auto-placement, no need to continue.
						break;
					}
				}

				// Found card being double clicked on, no need to continue.
				return;
			}
		}
	}

	// Maybe we are just clicking twice.
	VOnLMBPressed();
	VOnLMBPressed();
}

bool CSolitaireLogic::VStartDrag(const CPoint& _krDragStartPosition)
{
	// Immediately finish all lerping stacks.
	while(!m_lerpingStacks.empty()) {
		m_lerpingStacks.begin()->PushOntoDest();
		m_lerpingStacks.pop_front();
	}

	int iIndex = m_wasteStack.GetOverlapping(_krDragStartPosition);

	if(iIndex != -1 && iIndex == m_wasteStack.Size() - 1) {
		// Set the offset at which to drag at.
		m_dragOffset = _krDragStartPosition - m_wasteStack[iIndex].GetPosition();

		// Pop top card from drawn stack and push to deck.
		m_draggingStack.Push(m_wasteStack.PopStack(iIndex));
		//m_wasteStackFull.PopStack(iIndex);

		// Set the position of the cards.
		m_draggingStack.SetPosition(_krDragStartPosition - m_dragOffset);
		m_pDraggedFromStack = &m_wasteStack;

		return true;	// We are now dragging.
	}
	else {
		// Check if we are wanting to drag a buildable stack.
		for(unsigned int i = 0; i < m_buildStacks.size(); ++i) {
			iIndex = m_buildStacks[i].GetOverlapping(_krDragStartPosition);

			if(iIndex != -1 && m_buildStacks[i][iIndex].IsFaceUp()) {
				// Set the offset at which to drag at.
				m_dragOffset = _krDragStartPosition - m_buildStacks[i][iIndex].GetPosition();

				// Pop the stack at the overlapping index and push it onto the dragging stack.
				m_draggingStack.Push(m_buildStacks[i].PopStack(iIndex));

				// Set the position of the dragging stack.
				m_draggingStack.SetPosition(_krDragStartPosition - m_dragOffset);
				m_pDraggedFromStack = &m_buildStacks[i];

				return true;
			}
		}

		for(unsigned int i = 0; i < m_winStacks.size(); ++i) {
			iIndex = m_winStacks[i].GetOverlapping(_krDragStartPosition);

			if(iIndex != -1 && m_winStacks[i][iIndex].IsFaceUp()) {
				// Set the offset at which to drag at.
				m_dragOffset = _krDragStartPosition - m_winStacks[i][iIndex].GetPosition();

				// Pop the stack at the overlapping index and push it onto the dragging stack.
				m_draggingStack.Push(m_winStacks[i].PopStack(iIndex));

				// Set the position of the dragging stack.
				m_draggingStack.SetPosition(_krDragStartPosition - m_dragOffset);
				m_pDraggedFromStack = &m_winStacks[i];

				return true;
			}
		}
	}

	return false;
}

void CSolitaireLogic::VReleaseDrag()
{
	// Immediately finish all lerping stacks.
	while(!m_lerpingStacks.empty()) {
		m_lerpingStacks.begin()->PushOntoDest();
		m_lerpingStacks.pop_front();
	}

	if(!m_draggingStack.IsEmpty()) {
		for(unsigned int i = 0; i < m_buildStacks.size(); ++i) {
			if(m_buildStacks[i].IsOverlapping(m_draggingStack[0].GetRect())) {
			// Dragging stack overlaps build stack.
				if(m_buildStacks[i].CanBePushed(m_draggingStack.GetStack())) {
				// Dragging stack can be pushed onto the build stack.
					if(i < m_buildStacks.size() - 1 && m_buildStacks[i + 1].CanBePushed(m_draggingStack.GetStack())) {
					// We might be trying to drop to the stack on right instead.
						// Get the areas of overlap.
						int iOverlapLeft = AreaOfOverlap(m_buildStacks[i].GetTopRect(), m_draggingStack[0].GetRect());
						int iOverlapRight = AreaOfOverlap(m_buildStacks[i + 1].GetTopRect(), m_draggingStack[0].GetRect());

						if(iOverlapLeft >= iOverlapRight) {
						// The user is favouring the left build stack.
							m_lerpingStacks.push_back(CLerpStack(m_draggingStack.PopSubStack(), &m_buildStacks[i]));
						}
						else {
						// The user is favouring the right build stack.
							m_lerpingStacks.push_back(CLerpStack(m_draggingStack.PopSubStack(), &m_buildStacks[i + 1]));
						}
					}
					else {
						// Drop cards here.
						m_lerpingStacks.push_back(CLerpStack(m_draggingStack.PopSubStack(), &m_buildStacks[i]));
					}

					// Update scoring.
					if(m_pDraggedFromStack == &m_wasteStack) {
					// We've transfered from the waste stack to a build stack.
						m_iScore += g_kiSCORE_WASTE_BUILD;

						m_wasteStack.VUpdatePositions();
					}
					else {
						for(unsigned int i = 0; i < m_winStacks.size(); ++i) {
							if(m_pDraggedFromStack == &m_winStacks[i]) {
							// We've transfered from a win stack to a build stack.
								m_iScore += g_kiSCORE_WIN_BUILD;

								break;
							}
						}
					}

					return;
				}
				else {
				// Can't be dropped here.
					// We might be trying to drop to the stack on right instead.
					if(i < m_buildStacks.size() - 1 && 
					   m_buildStacks[i + 1].IsOverlapping(m_draggingStack[0].GetRect()) && 
					   m_buildStacks[i + 1].CanBePushed(m_draggingStack.GetStack())) {
						// Drop stack here.
						m_lerpingStacks.push_back(CLerpStack(m_draggingStack.PopSubStack(), &m_buildStacks[i + 1]));
						return;
					}
					break;
				}
			}
		}

		for(unsigned int i = 0; i < m_winStacks.size(); ++i) {
			if(m_winStacks[i].IsOverlapping(m_draggingStack[0].GetRect())) {
			// Dragging stack overlaps build stack.
				// Drop cards here.
				if(m_winStacks[i].CanBePushed(m_draggingStack.GetStack())) {
				// Dragging stack can be pused onto the win stack.
					if(i < m_winStacks.size() - 1 && m_winStacks[i + 1].CanBePushed(m_draggingStack.GetStack())) {
					// We might be trying to drop to the stack on right instead.
						// Get the areas of overlap.
						int iOverlapLeft = AreaOfOverlap(m_winStacks[i].GetTopRect(), m_draggingStack[0].GetRect());
						int iOverlapRight = AreaOfOverlap(m_winStacks[i + 1].GetTopRect(), m_draggingStack[0].GetRect());

						if(iOverlapLeft >= iOverlapRight) {
						// The user is favouring the left build stack.
							m_lerpingStacks.push_back(CLerpStack(m_draggingStack.PopSubStack(), &m_winStacks[i]));
							//m_winStacks[i].Push(m_draggingStack.PopStack(0));
						}
						else {
						// The user is favouring the right build stack.
							//m_winStacks[i + 1].Push(m_draggingStack.PopStack(0));
							m_lerpingStacks.push_back(CLerpStack(m_draggingStack.PopSubStack(), &m_winStacks[i + 1]));
						}
					}
					else {
						// Drop cards here.
						m_lerpingStacks.push_back(CLerpStack(m_draggingStack.PopSubStack(), &m_winStacks[i]));
						//m_winStacks[i].Push(m_draggingStack.PopStack(0));
					}

					// Update scoring.
					if(m_pDraggedFromStack == &m_wasteStack) {
					// We've transfered from the waste stack to a win stack.
						m_iScore += g_kiSCORE_WASTE_WIN;

						m_wasteStack.VUpdatePositions();
					}
					else {
						for(unsigned int i = 0; i < m_buildStacks.size(); ++i) {
							if(m_pDraggedFromStack == &m_buildStacks[i]) {
							// We've transfered from a build stack to a win stack.
								m_iScore += g_kiSCORE_BUILD_WIN;

								break;
							}
						}
					}

					return;
				} 
				else {
				// Can't be dropped here.
					// We might be trying to drop to the stack on right instead.
					if(i < m_winStacks.size() - 1 &&
					   m_winStacks[i + 1].IsOverlapping(m_draggingStack[0].GetRect()) &&
					   m_winStacks[i + 1].CanBePushed(m_draggingStack.GetStack())) {
						//m_winStacks[i + 1].Push(m_draggingStack.PopStack(0));
						m_lerpingStacks.push_back(CLerpStack(m_draggingStack.PopSubStack(), &m_winStacks[i + 1]));
						return;
					}

					break;
				}
			}
		}


		// Couldn't find location to drop cards, send dragging stack back.
		if(m_pDraggedFromStack) {
			m_lerpingStacks.push_back(CLerpStack(m_draggingStack, m_pDraggedFromStack));

			m_draggingStack.Clear();

			m_pDraggedFromStack = NULL;	// No longer are we dragging from this stack.
		}
		else {
		// m_pDraggedFromStack is NULL.
			// Put card into default stack.
			//m_wasteStackFull.Push(m_draggingStack.PopStack(0)); // [ERR]
			m_wasteStack.Push(m_draggingStack.PopStack(0));
		}

		// Clear the dragging stack as it's been dropped.
		m_draggingStack.Clear();
	}
}

void CSolitaireLogic::SetCardDrawAmount(int _iCardDrawAmount)
{
	m_iCardDrawQueuedAmount = _iCardDrawAmount;

	if(m_iCardDrawAmount != m_iCardDrawQueuedAmount) {
		std::wstring wsTextOut = L"These settings won't apply to the game in progress, do you want to start a new one?";
		int iResult = MessageBox(g_pApp->GetHwnd(), wsTextOut.c_str(), L"New Settings", MB_YESNO);

		if(iResult == IDYES) {
			SetupGame();
		}
		else {
		// The user did not want to start a new game.
		}
	}
}

void CSolitaireLogic::SetupGame()
{
	m_iCardDrawAmount = m_iCardDrawQueuedAmount;
	// Reset score and time.
	m_iScore = 0;
	m_fElapsedTime = 0;
	m_bHasWon = false;

	// Clear all stacks.
	m_buildStacks.clear();
	m_winStacks.clear();

	m_deckStack = CShowStack(CPoint(g_kiLAYOUT_OFFSET, g_kiLAYOUT_OFFSET), 5, 2, keBoth);
	m_wasteStack = CShowStack(CPoint(m_deckStack.GetPosition().m_iX + g_kiCARD_WIDTH + g_kiLAYOUT_OFFSET, 
										g_kiLAYOUT_OFFSET), m_iCardDrawAmount, g_kiSTACK_OFFSET, keRight);
	m_draggingStack = CCardStack(CPoint(0, 0), g_kiSTACK_OFFSET);
	/*m_miniStack = CCardStack(CPoint(m_deckStack.GetPosition().m_iX + g_kiCARD_WIDTH + g_kiLAYOUT_OFFSET, 
										g_kiLAYOUT_OFFSET), g_kiSTACK_OFFSET, keRight);*/

	CRect tempRect;

	// Create all cards.
	for(int i = 0; i < g_kiNO_OF_SUITS; ++i)
	{
		for(int j = 0; j < g_kiNO_OF_VALUES; ++j)
		{
			// Create the mask and add it to the sprite sheet.
			tempRect = CRect(g_kiCARD_WIDTH * j, g_kiCARD_HEIGHT * i, g_kiCARD_WIDTH, g_kiCARD_HEIGHT);
			m_cardSprites.AddMask(tempRect);

			// Make the card and add it to the deck of cards.
			m_deckStack.Push(CPlayingCard(static_cast<ECardValue>(j + 1), 
										  static_cast<ECardSuit>(i), 
									      tempRect.GetPosition(), false));
		}
	}

	// Shuffle the deck.
	m_deckStack.Shuffle();

	//==================================
	// BuildStacks
	//==================================
	int iBuildStackY = m_deckStack.GetPosition().m_iY + g_kiCARD_HEIGHT + g_kiLAYOUT_OFFSET;

	for(int i = 0; i < g_kiNO_OF_BUILD_STACKS; ++i) {
		CPoint currentPosition(g_kiLAYOUT_OFFSET + (g_kiLAYOUT_OFFSET + g_kiCARD_WIDTH) * i, iBuildStackY); 

		CBuildStack newStack(currentPosition, g_kiSTACK_OFFSET);
		m_buildStacks.push_back(newStack);
	}

	float fLerpWaitTime = 0;

	//m_deckStack.SetNoOfCardsToShow(52);

	for(unsigned int i = 0; i < m_buildStacks.size(); ++i) {
		for(int j = m_buildStacks.size() - 1; j >= 0; --j) {
			// Take the top card from the deck.
			bool bFlipOnceDone = false;
			if(i == j) {
			// This is the last card to move to this column.
				// Flip the last card on each buildable stack.
				bFlipOnceDone = true;
			}

			m_lerpingStacks.push_back(CLerpStack(m_deckStack.Peek(), &m_buildStacks[j], fLerpWaitTime, bFlipOnceDone));
			fLerpWaitTime += g_kfSTANDARD_LERP_WAITTIME;

			// Remove top card from the deck;
			m_deckStack.Pop();
			m_deckStack.VUpdatePositions();

			if(i == j) {
			// This is the last card to move to this column.
				// Flip the last card on each buildable stack.
				break;
			}
		}
	}

	//==================================
	// WinStacks
	//==================================

	int iWinStacksOffsetX = g_kiNO_OF_BUILD_STACKS - g_kiNO_OF_WIN_STACKS;

	for(int i = 0; i < g_kiNO_OF_WIN_STACKS; ++i) {
		// Calculate current X component of the position.
		int iCurrentXPos = g_kiLAYOUT_OFFSET + (g_kiLAYOUT_OFFSET + g_kiCARD_WIDTH) * (i + iWinStacksOffsetX);

		CPoint currentPosition(iCurrentXPos, g_kiLAYOUT_OFFSET); 

		// Create a new win stack at the desired position and add it to the list of win stacks.
		CWinStack newStack(currentPosition);
		m_winStacks.push_back(newStack);
	}
}

void CSolitaireLogic::DrawCard(const CPlayingCard& _krCard)
{
	// Get the mask of the card.
	int iMask = _krCard.GetValue() + _krCard.GetSuit() * (keKing /*+ 1*/);
	
	// Render.
	if(_krCard.IsFaceUp()) {
		g_pApp->RenderSprite(m_cardSprites, _krCard.GetPosition().m_iX, _krCard.GetPosition().m_iY, iMask);
	}
	else {
		g_pApp->RenderSprite(m_cardBackSprite, _krCard.GetPosition().m_iX, _krCard.GetPosition().m_iY);
	}
}

void CSolitaireLogic::DrawStack(const CCardStack& _krStack)
{
	if(_krStack.IsEmpty()) {
	// No cards in the stack.
		// Nothing to draw.
	}
	else if(_krStack.GetOffset() == 0) {
	// The stack has no offset and all cards are at the same position.
		// Only need to draw the very top card.
		DrawCard(_krStack.Peek());
	}
	else {
		// Draw all the cards in the stack.
		for(int i = 0; i < _krStack.Size(); ++i) {
			DrawCard(_krStack[i]);
		}
	}
}

void CSolitaireLogic::DebugCard(const CPlayingCard& _krCard)
{
	//[DEBUG] - Show card status
	cout << endl;
	cout << "[Card Status]" << endl;
	cout << "Card Suit: " << _krCard.GetSuitString() << "\tCard Value: " << _krCard.GetValue() << endl;
	//cout << "Card Is Face Down: " << _krCard.IsFaceDown() << endl;
	if(_krCard.IsFaceUp())
	{
		cout << "Card Is Face Up: YES \tCard Position[" << _krCard.GetPosition().m_iX << "," << _krCard.GetPosition().m_iY << "]" << endl;
	}
	else
	{
		cout << "Card Is Face Up: NO \tCard Position[" << _krCard.GetPosition().m_iX << "," << _krCard.GetPosition().m_iY << "]" << endl;
	}
}